home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 4.iso
/
public
/
fax
/
src
/
util
/
SendFaxClient.c++
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-01
|
19KB
|
671 lines
/* $Header: /usr/people/sam/fax/util/RCS/SendFaxClient.c++,v 1.26 1994/03/09 18:46:28 sam Rel $ */
/*
* Copyright (c) 1990, 1991, 1992, 1993, 1994 Sam Leffler
* Copyright (c) 1991, 1992, 1993, 1994 Silicon Graphics, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that (i) the above copyright notices and this permission notice appear in
* all copies of the software and related documentation, and (ii) the names of
* Sam Leffler and Silicon Graphics may not be used in any advertising or
* publicity relating to the software without the specific, prior written
* permission of Sam Leffler and Silicon Graphics.
*
* THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
*
* IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
* ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <paths.h>
#include <osfcn.h>
#include <unistd.h>
#include <pwd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
extern "C" {
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> // XXX
}
#include <Dispatch/dispatcher.h>
#include "SendFaxClient.h"
#include "TypeRules.h"
#include "DialRules.h"
#include "PageSize.h"
#include "config.h"
struct FileInfo : public fxObj {
fxStr name;
const TypeRule* rule;
fxBool isTemp;
fxBool tagLine;
FileInfo();
~FileInfo();
};
fxDECLARE_ObjArray(FileInfoArray, FileInfo);
const fxStr SendFaxClient::TypeRulesFile(FAX_TYPERULES);
SendFaxClient::SendFaxClient()
{
typeRules = NULL;
dialRules = NULL;
files = new FileInfoArray;
pollCmd = FALSE;
coverSheet = TRUE;
gotPermission = FALSE;
permission = FALSE;
verbose = FALSE;
killtime = FAX_TIMEOUT; // default time to kill the job
hres = 204; // G3 standard
vres = FAX_DEFVRES; // default resolution
pageWidth = 0;
pageLength = 0;
totalPages = 0;
maxRetries = -1;
notify = FAX_DEFNOTIFY; // default notification
setup = FALSE;
}
SendFaxClient::~SendFaxClient()
{
u_int i;
for (i = 0; i < coverPages.length(); i++)
unlink((char*) coverPages[i]);
for (i = 0; i < tempFiles.length(); i++)
unlink((char*) tempFiles[i]);
delete typeRules;
delete dialRules;
delete files;
}
fxBool
SendFaxClient::prepareSubmission()
{
u_int i, n;
if (!setupSenderIdentity(from))
return (FALSE);
if (pageSize == "" && !setPageSize("default"))
return (FALSE);
typeRules = TypeRules::read(fxStr(FAX_LIBDATA) | "/" | TypeRulesFile);
if (!typeRules) {
printError("Unable to setup file typing and conversion rules");
return (FALSE);
}
typeRules->setVerbose(verbose);
dialRules = new DialStringRules(fxStr(FAX_LIBDATA) | "/" | FAX_DIALRULES);
dialRules->setVerbose(verbose);
if (!dialRules->parse() && verbose) // NB: not fatal
printWarning("unable to setup dialstring rules");
for (i = 0, n = files->length(); i < n; i++)
if (!handleFile((*files)[i]))
return (FALSE);
/*
* Convert dialstrings to a displayable format. This
* deals with problems like calling card access codes
* getting stuck on the cover sheet and/or displayed in
* status messages.
*/
externalNumbers.resize(destNumbers.length());
for (i = 0, n = destNumbers.length(); i < n; i++)
externalNumbers[i] = dialRules->displayNumber(destNumbers[i]);
/*
* Suppress the cover page if we're just doing a poll;
* otherwise, generate a cover sheet for each destination
* (We do it now so that we can be sure everything is ready
* to send before we setup a connection to the server.)
*/
if (pollCmd && files->length() == 0)
coverSheet = FALSE;
if (coverSheet) {
coverPages.resize(externalNumbers.length());
for (i = 0, n = externalNumbers.length(); i < n; i++)
coverPages[i] = makeCoverPage(destNames[i], externalNumbers[i],
destCompanys[i], destLocations[i], senderName);
}
return (setup = TRUE);
}
#define CHECK(x) { if (!(x)) return (FALSE); }
fxBool
SendFaxClient::submitJob()
{
CHECK(setup && callServer())
startRunning();
/*
* Explicitly check for permission to submit a job
* before sending the input documents. This way we
* we avoid sending a load of stuff just to find out
* that the user/host is not permitted to submit jobs.
*/
CHECK(sendLine("checkPerm", "send"))
permission = gotPermission = FALSE;
while (isRunning() && !getPeerDied() && !gotPermission)
Dispatcher::instance().dispatch();
CHECK(permission)
/*
* Transfer the document files first so that they
* can be referenced multiple times for different
* destinations.
*/
for (u_int i = 0; i < files->length(); i++) {
const FileInfo& info = (*files)[i];
if (info.rule->getResult() == TypeRule::TIFF)
CHECK(sendData("tiff", info.name))
else
CHECK(sendData("postscript", info.name))
}
if (pollCmd)
CHECK(sendLine("poll", ""))
for (i = 0; i < destNumbers.length(); i++) {
CHECK(sendLine("begin", i))
if (sendtime != "")
CHECK(sendLine("sendAt", sendtime))
if (maxRetries >= 0)
CHECK(sendLine("maxdials", maxRetries))
if (killtime != "")
CHECK(sendLine("killtime", killtime))
/*
* If the dialstring is different from the
* displayable number then pass both.
*/
if (destNumbers[i] != externalNumbers[i]) {
/*
* XXX
* We should bump the protocol version and
* warn the submitter if this isn't supported
* by the server.
*/
CHECK(sendLine("external", externalNumbers[i]))
}
CHECK(sendLine("number", destNumbers[i]))
CHECK(sendLine("sender", senderName))
CHECK(sendLine("mailaddr", mailbox))
if (jobtag != "")
CHECK(sendLine("jobtag", jobtag))
CHECK(sendLine("resolution", (int) vres))
CHECK(sendLine("pagewidth", (int) pageWidth))
CHECK(sendLine("pagelength", (int) pageLength))
if (notify == when_done)
CHECK(sendLine("notify", "when done"))
else if (notify == when_requeued)
CHECK(sendLine("notify", "when requeued"))
if (coverSheet) {
FILE* fp = fopen(coverPages[i], "r");
if (fp != NULL) {
sendLine("cover", 1); // cover sheet sub-protocol version 1
// copy prototype cover page
char line[1024];
while (fgets(line, sizeof (line)-1, fp)) {
char* cp = strchr(line, '\n');
if (cp)
*cp = '\0';
if (!sendCoverLine("%s", line))
break;
}
fclose(fp);
CHECK(sendLine("..\n"))
} else
printWarning(
"the cover page for \"%s\" disappeared; none was sent",
(char*) destNumbers[i]);
}
CHECK(sendLine("end", i))
}
return (sendLine(".\n"));
}
#undef CHECK
u_int SendFaxClient::getNumberOfDestinations() const
{ return destNames.length(); }
u_int SendFaxClient::getNumberOfFiles() const { return files->length(); }
u_int SendFaxClient::getTotalPages() const { return totalPages; }
void SendFaxClient::setCoverComments(const char* s) { comments = s; }
const fxStr& SendFaxClient::getCoverComments() const { return comments; }
void SendFaxClient::setCoverRegarding(const char* s) { regarding = s; }
const fxStr& SendFaxClient::getCoverRegarding() const { return regarding; }
void SendFaxClient::setCoverSheet(fxBool b) { coverSheet = b; }
void SendFaxClient::setResolution(float r) { vres = r; }
void SendFaxClient::setPollRequest(fxBool b) { pollCmd = b; }
fxBool SendFaxClient::getPollRequest() const { return pollCmd; }
void SendFaxClient::setNotification(FaxNotify n) { notify = n; }
void SendFaxClient::setKillTime(const char* s) { killtime = s; }
void SendFaxClient::setSendTime(const char* s) { sendtime = s; }
void SendFaxClient::setFromIdentity(const char* s) { from = s; }
void SendFaxClient::setJobTag(const char* s) { jobtag = s; }
const fxStr& SendFaxClient::getFromIdentity() const { return from; }
void SendFaxClient::setMaxRetries(int n) { maxRetries = n; }
void SendFaxClient::setVerbose(fxBool b) { verbose = b; }
fxBool SendFaxClient::getVerbose() const { return verbose; }
fxBool
SendFaxClient::setPageSize(const char* name)
{
PageSizeInfo* info = PageSizeInfo::getPageSizeByName(name);
if (info) {
pageWidth = info->width();
pageLength = info->height();
pageSize = name;
delete info;
return (TRUE);
} else {
printError("Unknown page size \"%s\"", name);
return (FALSE);
}
}
const fxStr& SendFaxClient::getPageSize() const { return pageSize; }
float SendFaxClient::getPageWidth() const { return pageWidth; }
float SendFaxClient::getPageLength() const { return pageLength; }
/*
* Add a new destination name and number.
*/
fxBool
SendFaxClient::addDestination(
const char* person,
const char* faxnum,
const char* company,
const char* location
)
{
if (!faxnum || faxnum[0] == '\0') {
printError("No fax number specified");
return (FALSE);
}
destNumbers.append(faxnum);
destNames.append(person ? person : "");
destLocations.append(location ? location : "");
destCompanys.append(company ? company : "");
return (TRUE);
}
/*
* Add a new file to send to each destination.
*/
void
SendFaxClient::addFile(const char* filename)
{
u_int i = files->length();
files->resize(i+1);
(*files)[i].name = filename;
}
/*
* Check parsed sender identity against identity obtained
* by looking at the real uid. If they're different and
* we are not running as a "trusted user", abort.
*/
fxBool
SendFaxClient::verifyPermission()
{
uid_t uid = getuid();
if (uid == 0)
return (TRUE);
passwd* pwd = getpwuid(uid);
fxStr name(pwd ? pwd->pw_name : "nobody");
if (name == "daemon" || name == "uucp" || name == "fax")
return (TRUE);
// check specified address against real uid's address
fxStr user(mailbox.head(mailbox.next(0, '@')));
user.remove(0, user.nextR(user.length(), '!'));
if (user != getUserName()) {
printError(
"Unauthorized attempt to set sender's identity (from %s, uid %s)",
(char*) user, (char*) getUserName());
return (FALSE);
}
return (TRUE);
}
/*
* Create the mail address for a local user.
*/
void
SendFaxClient::setMailbox(const char* user)
{
fxStr acct(user);
if (acct.next(0, '@') == acct.length()) {
char hostname[64];
(void) gethostname(hostname, sizeof (hostname));
struct hostent* hp = gethostbyname(hostname);
mailbox = acct | "@" | (hp ? hp->h_name : hostname);
} else
mailbox = acct;
}
const fxStr& SendFaxClient::getMailbox() const { return mailbox; }
/*
* Setup the sender's identity.
*/
fxBool
SendFaxClient::setupSenderIdentity(const fxStr& from)
{
FaxClient::setupUserIdentity(); // client identity
if (from != "") {
u_int l = from.next(0, '<');
if (l == from.length()) {
l = from.next(0, '(');
if (l != from.length()) { // joe@foobar (Joe Schmo)
mailbox = from.head(l);
l++; senderName = from.token(l, ')');
} else { // joe
setMailbox(from);
if (from == getUserName())
senderName = FaxClient::getSenderName();
else
senderName = "";
}
} else { // Joe Schmo <joe@foobar>
senderName = from.head(l);
l++; mailbox = from.token(l, '>');
}
if (senderName == "" && mailbox != "") {
/*
* Mail address, but no "real name"; construct one from
* the account name. Do this by first stripping anything
* to the right of an '@' and then stripping any leading
* uucp patch (host!host!...!user).
*/
senderName = mailbox;
senderName.resize(senderName.next(0, '@'));
senderName.remove(0, senderName.nextR(senderName.length(), '!'));
}
// strip and leading&trailing white space
senderName.remove(0, senderName.skip(0, " \t"));
senderName.resize(senderName.skipR(senderName.length(), " \t"));
mailbox.remove(0, mailbox.skip(0, " \t"));
mailbox.resize(mailbox.skipR(mailbox.length(), " \t"));
if (senderName == "" || mailbox == "") {
printError("Malformed (null) sender name or mail address");
return (FALSE);
}
if (!verifyPermission())
return (FALSE);
} else {
senderName = FaxClient::getSenderName();
setMailbox(getUserName());
}
return (TRUE);
}
const fxStr& SendFaxClient::getSenderName() const { return senderName; }
/*
* Process a file submitted for transmission.
*/
fxBool
SendFaxClient::handleFile(FileInfo& info)
{
info.rule = fileType(info.name);
if (!info.rule)
return (FALSE);
if (info.rule->getCmd() != "") { // conversion required
fxStr temp(_PATH_TMP "faxsndXXXXXX");
tempFiles.append(temp);
mktemp((char*) temp);
fxStr sysCmd = info.rule->getFmtdCmd(info.name, temp,
hres, vres, "1", pageSize);
if (verbose)
printf("CONVERT \"%s\"\n", (char*) sysCmd);
if (system((char*) sysCmd) != 0) {
unlink((char*) temp);
u_int ix = tempFiles.find(temp);
if (ix != fx_invalidArrayIndex)
tempFiles.remove(ix);
printError("Error converting data; command was \"%s\"",
(char*) sysCmd);
return (FALSE);
}
info.name = temp;
info.isTemp = TRUE;
} else // already postscript or tiff
info.isTemp = FALSE;
switch (info.rule->getResult()) {
case TypeRule::TIFF:
countTIFFPages(info.name);
break;
case TypeRule::POSTSCRIPT:
estimatePostScriptPages(info.name);
break;
}
return (TRUE);
}
/*
* Return a TypeRule for the specified file.
*/
const TypeRule*
SendFaxClient::fileType(const char* filename)
{
struct stat sb;
int fd = ::open(filename, O_RDONLY);
if (fd < 0) {
printError("%s: Can not open file", filename);
return (NULL);
}
if (fstat(fd, &sb) < 0) {
printError("%s: Can not stat file", filename);
::close(fd);
return (NULL);
}
if ((sb.st_mode & S_IFMT) != S_IFREG) {
printError("%s: Not a regular file", filename);
::close(fd);
return (NULL);
}
char buf[512];
int cc = read(fd, buf, sizeof (buf));
::close(fd);
if (cc == 0) {
printError("%s: Empty file", filename);
return (NULL);
}
const TypeRule* tr = typeRules->match(buf, cc);
if (!tr) {
printError("%s: Can not determine file type", filename);
return (NULL);
}
if (tr->getResult() == TypeRule::ERROR) {
fxStr emsg(tr->getErrMsg());
printError("%s: %s", filename, (char*) emsg);
return (NULL);
}
return tr;
}
#include "tiffio.h"
/*
* Count the number of ``pages'' in a TIFF file.
*/
void
SendFaxClient::countTIFFPages(const char* filename)
{
TIFF* tif = TIFFOpen(filename, "r");
if (tif) {
do {
totalPages++;
} while (TIFFReadDirectory(tif));
TIFFClose(tif);
}
}
/*
* Count the number of pages in a PostScript file.
* We can really only estimate the number as we
* depend on the DSC comments to figure this out.
*/
void
SendFaxClient::estimatePostScriptPages(const char* filename)
{
FILE* fd = fopen(filename, "r");
if (fd != NULL) {
char line[2048];
if (fgets(line, sizeof (line)-1, fd) != NULL) {
/*
* We only consider ``conforming'' PostScript documents.
*/
if (line[0] == '%' && line[1] == '!') {
int npagecom = 0; // # %%Page comments
int npages = 0; // # pages according to %%Pages comments
while (fgets(line, sizeof (line)-1, fd) != NULL) {
int n;
if (strncmp(line, "%%Page:", 7) == 0)
npagecom++;
else if (sscanf(line, "%%%%Pages: %u", &n) == 1)
npages += n;
}
/*
* Believe %%Pages comments over counting of %%Page comments.
*/
if (npages > 0)
totalPages += npages;
else if (npagecom > 0)
totalPages += npagecom;
}
}
fclose(fd);
}
}
/*
* Invoke the cover page generation program.
*/
fxStr
SendFaxClient::makeCoverPage(
const fxStr& name,
const fxStr& number,
const fxStr& company,
const fxStr& location,
const fxStr& sender
)
{
fxStr templ(_PATH_TMP "sndfaxXXXXXX");
tempFiles.append(templ);
int fd = mkstemp((char*) templ);
if (fd >= 0) {
fxStr cmd("faxcover");
if (getPageSize() != "default")
cmd.append(" -s " | getPageSize());
cmd.append(" -n \"" | number | "\"");
cmd.append(" -t \"" | name | "\"");
cmd.append(" -f \"" | sender | "\"");
if (getCoverComments() != "") {
cmd.append(" -c \"");
cmd.append(getCoverComments());
cmd.append("\"");
}
if (getCoverRegarding() != "") {
cmd.append(" -r \"");
cmd.append(getCoverRegarding());
cmd.append("\"");
}
if (location != "") {
cmd.append(" -l \"");
cmd.append(location);
cmd.append("\"");
}
if (company != "") {
cmd.append(" -x \"");
cmd.append(company);
cmd.append("\"");
}
if (getTotalPages() > 0) {
fxStr pages((int) getTotalPages(), "%u");
cmd.append(" -p " | pages);
}
if (getVerbose())
printf("COVER SHEET \"%s\"\n", (char*) cmd);
FILE* fp = popen(cmd, "r");
if (fp != NULL) {
// copy prototype cover page
char line[1024];
while (fgets(line, sizeof (line)-1, fp))
(void) write(fd, line, strlen(line));
if (pclose(fp) == 0) {
::close(fd);
return (templ);
}
}
printError("Error creating cover sheet; command was \"%s\"",
(char*) cmd);
} else
printError("%s: Can not create temporary file for cover sheet",
(char*) templ);
unlink((char*) templ);
templ = "";
return (templ);
}
fxBool
SendFaxClient::sendCoverLine(const char* va_alist ...)
#define fmt va_alist
{
va_list ap;
va_start(ap, va_alist);
char buf[4096];
vsprintf(buf, fmt, ap);
va_end(ap);
return sendLine("!", buf);
}
#undef fmt
#define isCmd(s) (strcasecmp(s, cmd) == 0)
void
SendFaxClient::recvConf(const char* cmd, const char* tag)
{
if (isCmd("permission")) {
gotPermission = TRUE;
permission = (strcasecmp(tag, "granted") == 0);
} else
printError("Unknown status message received: \"%s:%s\"", cmd, tag);
}
void
SendFaxClient::recvEof()
{
stopRunning();
}
void
SendFaxClient::recvError(const int err)
{
printError("Socket read error: %s", strerror(err));
stopRunning();
}
FileInfo::FileInfo()
{
isTemp = FALSE;
tagLine = TRUE;
rule = NULL;
}
FileInfo::~FileInfo()
{
if (isTemp)
unlink((char*) name);
}
fxIMPLEMENT_ObjArray(FileInfoArray, FileInfo);